﻿using ApiPlugin.Plugin;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;

namespace ApiPlugin
{
    public interface IPluginService
    {
        Task<string> AddPlugin(string fileName);

        Task<string> UpdatePlugin(string fileName);

        Task<string> RemovePlugin(string fileName);
    }

    public class PluginService : IPluginService
    {
        private readonly string _baseDir;
        private readonly ApplicationPartManager _applicationPartManager;
        private readonly CustomActionDescriptorChangeProvider _customActionDescriptorChangeProvider;
        private readonly ConcurrentDictionary<string, CustomAssemblyLoadContext> _loadContextDic;

        public PluginService(ApplicationPartManager applicationPartManager,
            CustomActionDescriptorChangeProvider customActionDescriptorChangeProvider)
        {
            _loadContextDic = new ConcurrentDictionary<string, CustomAssemblyLoadContext>();
            _baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            _applicationPartManager = applicationPartManager;
            _customActionDescriptorChangeProvider = customActionDescriptorChangeProvider;
        }

        public async Task<string> AddPlugin(string fileName)
        {
            var loadContext = new CustomAssemblyLoadContext();
            loadContext.Resolving += LoadContext_Resolving;
            AssemblyPart assemblyPart = new(loadContext.LoadFile($"{_baseDir}/{fileName}"));
            if (!_applicationPartManager.ApplicationParts.Any(m => m.Name == assemblyPart.Name))
            {
                _applicationPartManager.ApplicationParts.Add(assemblyPart);
                _customActionDescriptorChangeProvider.Notify();
            }
            else
            {
                return await Task.FromResult($"{ fileName}插件已存在");
            }

            return $"{ fileName}插件添加成功";
        }

        private Assembly LoadContext_Resolving(AssemblyLoadContext arg1, AssemblyName arg2)
        {
            var file = $"{_baseDir}/{arg2.Name}.dll";
            return ((CustomAssemblyLoadContext)arg1).LoadFile(file);
        }

        public Task<string> RemovePlugin(string fileName)
        {
            throw new NotImplementedException();
        }

        public Task<string> UpdatePlugin(string fileName)
        {
            throw new NotImplementedException();
        }
    }

    public class CustomAssemblyLoadContext : AssemblyLoadContext
    {
        public CustomAssemblyLoadContext() : base (true)
        {

        }

        public Assembly LoadFile(string path)
        {
            try
            {
                using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    return base.LoadFromStream(fs);
                }
            }
            catch
            {
                return null;
            }
        }
    }
}
